/*
 * Ext - JS Library 1.0 Alpha 3 - Rev 1
 * Copyright(c) 2006-2007, Jack Slocum.
 */

function refreshGrid(){
	Ext.onReady(Example.init, Example, true);
};
var hostDelay; // simulate delay from the host.  When the value is 0, the code completely bypasses the simulated host load.
var Example = {
    init : function(){
    	// The following value controls the height of the virtual grid.  When '30' is selected, the code uses preloaded data (for debugging purposes)
        //var numberOfRows = parseInt(document.getElementById('NumberOfRows').value);
        //hostDelay = parseInt(document.getElementById('HostDelay').value);
		var numberOfRows = 1000;
        // some data yanked off the web
        var myData = [
			['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
            ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
            ['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
            ['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
            ['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
            ['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
            ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
            ['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
            ['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
            ['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
            ['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
            ['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
            ['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
            ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
            ['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
            ['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
            ['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
            ['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
            ['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
            ['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
            ['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
            ['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
            ['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
            ['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
            ['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
            ['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
            ['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
            ['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
            ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am'],
            ['Walt Disney Company (The) (Holding Company)',29.89,0.24,0.81,'9/1 12:00am']
		];

        var ds = new Ext.data.VirtualStore({
        		length: numberOfRows,
		        proxy: new Ext.data.MemoryProxy(),
		        reader: new Ext.data.VirtualArrayReader({id: 0,length: numberOfRows}, [
                       {name: 'company'},
                       {name: 'price', type: 'float'},
                       {name: 'change', type: 'float'},
                       {name: 'pctChange', type: 'float'},
                       {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
                  ])
        });
        ds.load();
        ds.data.dummyData = new Ext.data.Store({
		        proxy: new Ext.data.MemoryProxy(myData),
		        reader: new Ext.data.ArrayReader({id: 0}, [
                       {name: 'company'},
                       {name: 'price', type: 'float'},
                       {name: 'change', type: 'float'},
                       {name: 'pctChange', type: 'float'},
                       {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
                  ])
        });
	    ds.data.dummyData.load();
        if (numberOfRows == 1000) {
        	ds = ds.data.dummyData;
 	    }else{
 	    	for (i = 0; i < ds.data.dummyData.data.items.length; i++){
 	    		ds.data.items[i] = ds.data.dummyData.data.items[i];
 	    	}
 	    }

		// example of custom renderer function
        function italic(value){
            return '' + value + '';
        }

		// example of custom renderer function
        function change(val){
            if(val > 0){
                return '<span style="color:green;">' + val + '</span>';
            }else if(val < 0){
                return '<span style="color:red;">' + val + '</span>';
            }
            return val;
        }
		// example of custom renderer function
        function pctChange(val){
		    if(val > 0){
		        return '<span style="color:green;">' + val + '%</span>';
		    }else if(val < 0){
		        return '<span style="color:red;">' + val + '%</span>';
		    }
		    return val;
		}

		// the DefaultColumnModel expects this blob to define columns. It can be extended to provide
        // custom or reusable ColumnModels
        var colArray = [
			{header: "Company", width: 200, sortable: true, locked:false, dataIndex: 'company'},
			{header: "Price", width: 75, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
			{header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
			{header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
			{header: "Last Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
		];
		
		// allocate excess width after resizing to the first column
		var excessWidth = document.getElementById("grid-example").clientWidth;
		for (i = 0; i < colArray.length; i++){
			excessWidth -= colArray[i].width;
		}
		if (excessWidth > 0){
			colArray[0].width += excessWidth;
		}
        var colModel = new Ext.grid.ColumnModel(colArray);
		
		// create the Grid
        var grid = new Ext.grid.VirtualGrid('grid-example', {
            ds: ds,
            cm: colModel
        });
        grid.render();

        grid.getSelectionModel().selectFirstRow();
    }
};

// This subclass of ArrayReader overloads the readRecords method to return an empty array and the specified value for totalRecords
Ext.data.VirtualArrayReader = function(meta, recordType){
    Ext.data.VirtualArrayReader.superclass.constructor.call(this, meta, recordType);
};

Ext.extend(Ext.data.VirtualArrayReader, Ext.data.ArrayReader, {
    readRecords : function(o){
	    return {
	        records : [],
	        totalRecords : this.meta.length
	    };
    }
});

// This subclass of Store overloads several methods so that existing code continues to function with data that is not completely loaded
Ext.data.VirtualStore = function() {
    Ext.data.VirtualStore.superclass.constructor.apply(this, arguments);
    this.data = new Ext.util.VirtualMixedCollection(false);
    this.data.length = this.length;
};

Ext.extend(Ext.data.VirtualStore, Ext.data.Store, {
    getAt : function(index){
    	var row;
    	if (!this.data.itemAt(index)){
			row=this.data.dummyData.data.itemAt(index % this.data.dummyData.data.length);
			row.loadNeeded = index;
			return row;
 	    }else{
	    	row = this.data.itemAt(index);
        	return row;
	    }
    },
	loadRow : function(row, view){
		if ((','+this.rowLoadSpec.toBeLoaded.join(',')+',').indexOf(','+row+',') == -1){ // avoiding array.IndexOf because it is not supported in IE
			var rowLoadSpec = this.rowLoadSpec;
			rowLoadSpec.toBeLoaded[rowLoadSpec.toBeLoaded.length] = row;
			if (!rowLoadSpec.requestProcessUnderway){
				rowLoadSpec.requestProcessUnderway = true;
            	rowLoadSpec.ds = this;
            	rowLoadSpec.view = view;
	            setTimeout(function(){
	            	rowLoadSpec.requestProcessUnderway = false;
	                rowLoadSpec.requestRows(rowLoadSpec.toBeLoaded.join(','));
					rowLoadSpec.toBeLoaded = [];
	            }, this.loadDelay || 10);
			}
		}
    },
    rowLoadSpec : {
    	toBeLoaded : [],
    	requestProcessUnderway : false,
		requestRows : function(rows){
			ds = this.ds;
			view = this.view;
			setTimeout(function(){
				requests = rows.split(',');
				for (i = 0; i < requests.length; i++){
					ds.data.items[requests[i]] = ds.data.dummyData.data.items[requests[i] % ds.data.dummyData.data.length];
				}
				view.refresh();
				//alert("requesting rows " + rows);
			},hostDelay || 1000);
	    }
    },
	getCount : function(){
		return this.length;
	}
});

// This subclass of MixedCollection overloads the clear and getRange methods so they work correctly with data that is not completely loaded
// It currently contains a dummyData property that is used solely for testing purposes
Ext.util.VirtualMixedCollection = function() {
    Ext.util.VirtualMixedCollection.superclass.constructor.apply(this, arguments);
};

Ext.extend(Ext.util.VirtualMixedCollection, Ext.util.MixedCollection, {
    clear : function(){
        //this.length = 0; We need to retain the length
        this.items = [];
        this.keys = [];
        this.map = {};
        this.fireEvent("clear");
    },
	getRange : function(start, end){
        var items = this.items;
        start = start || 0;
        end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
        var r = [];
        if(start <= end){
            for(var i = start; i <= end; i++) {
            	if (!items[i]){
					r[r.length] = this.dummyData.data.itemAt(i % this.dummyData.data.length);
					r[r.length-1].loadNeeded = i;
            	}else{
	        	    r[r.length] = items[i];
	        	    delete r[r.length-1].loadNeeded;
            	}
            }
        }else{
            for(var i = start; i >= end; i--) {
            	if (!items[i]){
         		    r[r.length] = this.dummyData.data.itemAt(i % this.dummyData.data.length);
					r[r.length-1].loadNeeded = i;
            	}else{
	        	    r[r.length] = items[i];
	        	    delete r[r.length-1].loadNeededed;
            	}
            }
        }
        return r;
    }
});

// This subclass of Grid substitutes the VirtualGridView and VirtualRowSelectionModel.  It chains a renderer that detects when new data is needed
// This mechanism might not be needed if the grid supported a custom row renderer.  A rowLoading template would also be useful.
Ext.grid.VirtualGrid = function() {
    Ext.grid.VirtualGrid.superclass.constructor.apply(this, arguments);
    var config = this.colModel.config;
    for (i = 0; i < config.length; i++){
    	var grid = this;
    	config[i].origRenderer = config[i].renderer;
    	config[i].renderer = function(value, p, r, rowIndex, i, ds) {
    		if (r.loadNeeded){
    			if (hostDelay != 0){
	    			ds.loadRow(rowIndex, grid.getView());
	    			return (i == 0) ? "loading "+r.loadNeeded+" ..." : "";
    			}else{
    				if (typeof(config[i].origRenderer) == "undefined"){
		    			return Ext.grid.ColumnModel.defaultRenderer(value);
    				}else{
	    				return config[i].origRenderer((i==2)? r.loadNeeded : value, p, r, rowIndex, i, ds);
    				}
    			}
    		}else{
	    		if (typeof(config[i].origRenderer) == "undefined"){
	    			return Ext.grid.ColumnModel.defaultRenderer(value);
	    		}else{
	    			return config[i].origRenderer(value, p, r, rowIndex, i, ds);
	    		}
    		}
    	}
    }
};

Ext.extend(Ext.grid.VirtualGrid, Ext.grid.Grid, {
    getSelectionModel : function(){
        if(!this.selModel){
            this.selModel = new Ext.grid.VirtualRowSelectionModel();
        }
        return this.selModel;
    },
    getView: function(){
        if(!this.view){
            this.view = new Ext.grid.VirtualGridView();
        }
        return this.view;
    }
});

// This subclass of RowSelectionModel overrides the onRefresh method to handle selections relative to the value of view.topRow
// It currently only works correctly when the data has been preloaded (number of rows == 30)
Ext.grid.VirtualRowSelectionModel = function(){
    Ext.grid.VirtualRowSelectionModel.superclass.constructor.apply(this, arguments);
}

Ext.extend(Ext.grid.VirtualRowSelectionModel, Ext.grid.RowSelectionModel, {
	onRefresh : function(){
        var ds = this.grid.dataSource, i, v = this.grid.view;
        var s = this.selections;
        s.each(function(r){
            if((i = ds.indexOfId(r.id)) != -1){
            	i -= v.topRow;
            	if (i >= 0 && i < (v.topRow + v.viewRows - 1)){
                	v.onRowSelect(i);
            	}
            }else{
                s.remove(r);
            }
        });
    }
});

// This subclass of GridView overrides several methods to virtualize the grid.  The 
Ext.grid.VirtualGridView = function() {
    Ext.grid.VirtualGridView.superclass.constructor.apply(this, arguments);
    var tpls = this.templates || {};
    tpls.master = new Ext.Template(
       '<div class="x-grid" hidefocus="true" style="overflow:hidden">',
          '<div class="x-grid-topbar"></div>',
          '<div class="x-grid-hscroller" style="overflow-x:auto;overflow-y:hidden"><div></div></div>',
          '<div class="x-grid-locked">',
              '<div class="x-grid-header">{lockedHeader}</div>',
              '<div class="x-grid-body">{lockedBody}</div>',
          '</div>',
          '<div class="x-grid-viewport">',
              '<div class="x-grid-header">{header}</div>',
              '<div class="x-grid-body">{body}</div>',
// the following div contains an image that is resized to the pixel height of the virtual grid (row height * number of rows).  The overflow style creates a scrollbar that accurately reflects the ratio of displayed text to the total height.
              '<div class="x-grid-vscroller" tabindex="-1" style="scrollbar-base-color:#D4D5CB; height:100%; top:0; position:absolute; overflow: auto; left: auto; width: 17px; outline-color: invert; outline-style: none; outline-width: medium; right: -1px;">[img]spacer.gif[/img]</div>',
          '</div>',
          '<div class="x-grid-bottombar"></div>',
          '',
          '<div class="x-grid-resize-proxy"> </div>',
       "</div>"
    );
    tpls.master.disableformats = true;
    this.templates = tpls;
};

Ext.extend(Ext.grid.VirtualGridView, Ext.grid.GridView, {
	initElements : function(){
	    var E = Ext.Element;
	    var el = this.grid.container.dom.firstChild;
	    var cs = el.childNodes;
	    
	    this.el = new E(el);
	    this.headerPanel = new E(el.firstChild);
	    this.headerPanel.enableDisplayMode("block");

	    this.scroller = new E(cs[1]);
	    this.scrollSizer = new E(this.scroller.dom.firstChild);
	    
	    this.lockedWrap = new E(cs[2]);
	    this.lockedHd = new E(this.lockedWrap.dom.firstChild);
	    this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
	    
	    this.mainWrap = new E(cs[3]);
	    this.mainHd = new E(this.mainWrap.dom.firstChild);
	    this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
	    this.vscroller = new E(this.mainWrap.dom.lastChild);
          this.vscroller.on("scroll", this.handleScroll, this);

	    this.footerPanel = new E(cs[4]);
	    this.footerPanel.enableDisplayMode("block");

	    this.focusEl = new E(cs[5]);
	    this.focusEl.swallowEvent("click", true);
	    this.resizeProxy = new E(cs[6]);
	    
	    this.headerSelector = String.format(
	       '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
	       this.lockedHd.id, this.mainHd.id
	    );
	    
	    this.splitterSelector = String.format(
	       '#{0} div.x-grid-split, #{1} div.x-grid-split',
	       this.lockedHd.id, this.mainHd.id
	    );
	    
	    if (typeof this.topRow == "undefinded") {
	    	this.topRow = -1;
	    }
    },
	syncScroll : function(){
	    var sb = this.scroller.dom;
	    var vsb = this.vscroller.dom;
	    var sh = this.mainHd.dom;
	    var bs = this.mainBody.dom;
	    var lv = this.lockedBody.dom;
	    sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;	    
	    if (!this.rowPixelHeight || parseInt(vsb.scrollTop/this.rowPixelHeight) != this.topRow){
	    	this.refresh();
	    }else{
	    	lv.scrollTop = bs.scrollTop = vsb.scrollTop % this.rowPixelHeight;
	    }
	},
	handleScroll: function(e){
	    this.syncScroll();
	    var sb = this.scroller.dom;
	    var vsb = this.vscroller.dom;
	    this.grid.fireEvent("bodyscroll", sb.scrollLeft, vsb.scrollTop);
	    e.stopEvent();
    },
    renderBody : function(){
	    var bs = this.mainBody.dom;
	    var lv = this.lockedBody.dom;
	    var bt = this.templates.body;
	    var vsb = this.vscroller.dom;
	    if (!this.rowPixelHeight){
	    	markup = this.renderRows(0,0);
	    	this.mainBody.update(bt.apply({rows: markup[1]}));
	    	this.rowPixelHeight = bs.firstChild.clientHeight;
	    	this.viewRows = parseInt(bs.clientHeight/this.rowPixelHeight)+2;
	    	vsb.firstChild.style.height = ((this.ds.data.length + 1) * this.rowPixelHeight) + "px";
	    }
	    this.topRow  = parseInt(vsb.scrollTop/this.rowPixelHeight);
		lv.scrollTop = bs.scrollTop = vsb.scrollTop % this.rowPixelHeight;	     
        var markup = this.renderRows(this.topRow, this.topRow + this.viewRows);
        return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
    }
});

var ResizableExample = {
    init : function(){
        var dwrapped = new Ext.Resizable('grid-example', {
            wrap:true,
            pinned:true,
            width:535,
            height:225,
            minWidth:200,
            minHeight: 50,
            dynamic: true
        });
        dwrapped.on("resize", function(){
        	refreshGrid();
        });
      }
};
Ext.EventManager.onDocumentReady(ResizableExample.init, ResizableExample, true);
Ext.onReady(Example.init, Example, true);